﻿/*
NOTE:
Read XML by easy way.
include Loading and dispatch event, by "com.cf.core.events.LoadEvent"


Usage:
var abc:cacheXML = new cacheXML();
addEventListener(LoadEvent.ONLOAD, handleLoaded);	// File will be loading, and will response after complete.
abc.loadXMLByFile("yourpath.xml"); // your file path, and it's loading.
// or 
abc.loadXMLByString("<doc>..................</doc>"); // you can just type in the string by XML format
abc.retry(10);	// give function a max retry counter, for downloading the target file.
abc.RELEASE();
//////////////
// in handleLoaded function it you can read this.
abc.xml; // return by XML format
abc.obj; // return by Object format
abc.length; // return by String, the XML nodes number
abc.type; // nothing, just return where is the XML loaded by.
abc.info; // return String, to display the whole XML's node value.

*/
package com.cf.core.Utils{
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.events.EventDispatcher;
	import flash.events.HTTPStatusEvent;
	import flash.events.IEventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	//import flash.xml.*;
	import com.cf.core.events.LoadEvent;
	import com.cf.core.Utils.tools;
	import com.cf.core.Utils.XMLToObject;
	// import cacheIMG
	// import cacheXML
	public class cacheXML extends EventDispatcher {
		private var __type:String;			// to record this XML data is gen by what. [URL/String]
		private var __XMLUrlReq:URLRequest;
		private var __XMLLoader:URLLoader;
		private var __XMLData:XML;
		private var __isUse:Boolean=false;
		private var __failTolerance:uint;
		private var __failCount:uint;
		private var __data:Object;
		
		public function cacheXML(){
			retry(2);
			RELEASE();
		}//fn
		public function retry(_n:uint):void{__failTolerance=_n;}//fn
		public function RELEASE():void{__failCount=1;__isUse=false;__type="";__data=null;}//fn
		public function loadXMLByString(_str:String):void{
			if(!isUse){
				RELEASE();
				__isUse=true;
				__type="String";
				try{
					__XMLData = new XML(_str); // xml
					__data = XMLToObject.parse(__XMLData); // xml2Obj
					//trace( this.info );
					dispatchEvent(new LoadEvent(LoadEvent.ONLOAD, __data));
				}catch(e:Error){
					dispatchEvent(new LoadEvent(LoadEvent.FAIL, {msg: "Error unknow Format XML."}));
				}//try
			}else{
				// The String format.
				dispatchEvent(new LoadEvent(LoadEvent.FAIL, {msg: "Error: access by other."}));
			}//if
			__isUse=false;
		}//fn
		
		public function loadXMLByFile(_addr:String):void {
			if(!isUse){
				RELEASE();
				__isUse=true;
				__type="File";
				__XMLLoader=new URLLoader();
				__XMLUrlReq=new URLRequest(_addr);
				// Listener for file load.
				configureListeners(__XMLLoader);
				__XMLLoader.load(__XMLUrlReq);
			}else{
				if(_addr==__XMLUrlReq.url && __type=="File"){
					// reload
					configureListeners(__XMLLoader);
					__XMLLoader.load(__XMLUrlReq);
				}else{
					// double access.
					dispatchEvent(new LoadEvent(LoadEvent.FAIL, {msg: "Error: access by other"}));
				}//if
			}//if
		}//fn
		
		////////////////////////
		//
		// Getter Setter
		//
		////////////////////////
		// public function set isUse(_t:Boolean):void{__isUse=_t}
		public function get isUse():Boolean{return __isUse}
		public function get xml():XML{return __XMLData}
		public function set xml(_rslt:XML){__XMLData=_rslt}
		public function get obj():Object{if(!isUse && __XMLData) return XMLToObject.parse(__XMLData); else return Object(undefined)}
		
		public function get length():int{
			// little candy tools. __XMLData.length, AS3 will thing there is the XML's node name.
			// for that we needed to call like this to return int __XMLData.length() <- function type.
			// what ever, we may not to able the type for THIS class, so just make a simple way to change the length.
			for(var prop in __XMLData.children());
			return int(prop)+1;
		}//fn
		
		//
		// where is the information coming form.
		public function get type():String{
			switch(__type){
				case "String": case "File": return __type; break;
				default: return "undefined"; break;
			}//case
		}//fn
		
		//
		// return little report for this XMLdata.
		// May release to public use for later version.
		public function get info():String{
			var tmp:String;
			// Show the XML data info.
			
			tmp=this+"loadXMLBy"+this.type+((this.type=="File")?"Url:\""+__XMLUrlReq.url+"\"":"")+"\r\troot =  <"+__XMLData.name().localName+"> Info.\r\t";
			tmp+="nodes on <"+__XMLData.name().localName+"> = "+this.length +" {\n";
			tmp+=tools.varInfo(this.obj);
			// for(var prop in __XMLData.children()){
			//	 tmp+="<"+__XMLData.child(prop).name().localName +">,";
			// }//for
			tmp=tmp.substr(0,-1)+"\n}\r";
			return tmp;
		}//fn
		
		
		//
		//private AREA==========================================================
		//
		private function configureListeners(dispatcher:IEventDispatcher):void{
			dispatcher.addEventListener(Event.COMPLETE, handleXMLFileloaded);
            dispatcher.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handleSecurityError);
            dispatcher.addEventListener(IOErrorEvent.IO_ERROR, handleIOError);
		}
		
		private function deConfigureListeners(dispatcher:IEventDispatcher):void{
			dispatcher.removeEventListener(Event.COMPLETE, handleXMLFileloaded);
			dispatcher.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, handleSecurityError);
            dispatcher.removeEventListener(IOErrorEvent.IO_ERROR, handleIOError);
		}
		private function load_retry():void{
			if(__type=="File") {
				dispatchEvent(new LoadEvent(LoadEvent.FAIL, {msg: "WARN : Load ["+__XMLUrlReq.url+"] retry("+__failCount+")..."}));
				__failCount++;
				loadXMLByFile(__XMLUrlReq.url);
			}//if
		}//fn
		
		protected function handleIOError(e:IOErrorEvent):void{
			deConfigureListeners(IEventDispatcher(e.target));
			if(__failCount <= __failTolerance){
				load_retry();
			}else{
				__isUse=false;
				dispatchEvent(new LoadEvent(LoadEvent.FAIL, {msg: "FAIL : Load ["+__XMLUrlReq.url+"] handleXMLError reach MAX retry counter"}));
			}//if
		}//fn
        protected function handleSecurityError(e:SecurityErrorEvent):void{
			deConfigureListeners(IEventDispatcher(e.target));
			if(__failCount <= __failTolerance){
				load_retry();
			}else{
				__isUse=false;
				dispatchEvent(new LoadEvent(LoadEvent.FAIL, {msg: "FAIL : Load ["+__XMLUrlReq.url+"] handleSecurityError reach MAX retry counter"}));
			}//if
        }//fn
		protected function handleXMLFileloaded(e:Event):void{
			deConfigureListeners(IEventDispatcher(e.target));
			// Reference the XML data from the target.
			__XMLData=new XML(e.target.data); // xml
			__data = XMLToObject.parse(__XMLData); // xml2Obj
			// trace( this.info );
			// Notify that everything is set up correctly
			// by firing an asynchronous event.
			__isUse=false;
			dispatchEvent(new LoadEvent(LoadEvent.ONLOAD, __data));
		}//fn
	}//class
}//package